package org.summer.gateway.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.ZoneId;
import java.util.UUID;

/**
 * 默认jwt解析器
 * @author wjj9132
 * @date 2024/9/26 11:00
 * @since 1.0
 */
@Log4j2
@Component
public class DefaultJsonWebTokenResolver implements JsonWebTokenResolver<JsonWebToken> {
    private final JsonWebTokenProperties properties;
    private final Algorithm algorithm;

    @Autowired
    public DefaultJsonWebTokenResolver(JsonWebTokenProperties properties) {
        this.properties = properties;
        this.algorithm = Algorithm.HMAC256(properties.getSecret());
    }

    /**
     * 创建jwt令牌,根据payload中的username
     * @param payload 需要创建token携带的有效信息
     * @return 生成的jwt令牌信息,如果出错则error为true
     */
    @Override
    public JsonWebToken createToken(JsonWebToken payload) {
        log.debug("创建jwt令牌: {}", payload);
        if (payload.getUsername() == null || payload.getUsername().isEmpty()) {
            log.warn("创建jwt令牌失败,用户名不能为空");
            payload.setError(true);
            payload.setErrorMessage("创建jwt令牌失败,用户名不能为空");
            return payload;
        }
        String tokenId = UUID.randomUUID().toString();
        Instant created = Instant.now();
        Instant expire = created.plusSeconds(this.properties.getExpireTime());
        JWTCreator.Builder builder = JWT.create()
                .withJWTId(this.properties.getJwtId())
                .withIssuer(this.properties.getIssuer())
                .withAudience(this.properties.getAudience())
                .withIssuedAt(created)
                .withExpiresAt(expire)
                .withSubject(payload.getUsername())
                .withClaim(TOKEN_ID_KEY, tokenId);
      try {
          String token = TOKEN_PREFIX + builder.sign(this.algorithm);
          log.debug("创建jwt令牌成功: {}", token);
          payload.setToken(token);
          payload.setTokenId(tokenId);
          payload.setCreateTime(created.atZone(ZoneId.systemDefault()).toLocalDateTime());
          payload.setExpireTime(expire.atZone(ZoneId.systemDefault()).toLocalDateTime());
          payload.setExpireSeconds(this.properties.getExpireTime());
          payload.setError(false);
      }catch (JWTCreationException e){
          log.error("创建jwt令牌失败",e);
          payload.setError(true);
          payload.setErrorMessage(e.getMessage());
      }catch (IllegalArgumentException e){
          log.error("创建用户令牌参数非法",e);
          payload.setError(true);
          payload.setErrorMessage(e.getMessage());
      }
        return payload;
    }

    @Override
    public JsonWebToken parseToken(String token) {
        JsonWebToken jsonWebToken = new JsonWebToken();
        if (token == null || token.isEmpty()) {
            log.error("token解析失败,token为空");
            jsonWebToken.setError(true);
            jsonWebToken.setErrorMessage("token解析失败,token为空");
            return jsonWebToken;
        }
        if (!token.startsWith(TOKEN_PREFIX)) {
            log.error("token解析失败,token格式错误");
            jsonWebToken.setError(true);
            jsonWebToken.setErrorMessage("token解析失败,token格式错误");
            return jsonWebToken;
        }
        try {
            DecodedJWT verify = JWT.require(this.algorithm)
                    .withJWTId(this.properties.getJwtId())
                    .withIssuer(this.properties.getIssuer())
                    .build().verify(token.substring(TOKEN_PREFIX.length()));
            jsonWebToken.setCreateTime(verify.getIssuedAtAsInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            jsonWebToken.setExpireTime(verify.getExpiresAtAsInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            jsonWebToken.setUsername(verify.getSubject());
            jsonWebToken.setTokenId(verify.getClaim(TOKEN_ID_KEY).asString());
            jsonWebToken.setError(false);
            jsonWebToken.setExpireSeconds(properties.getExpireTime());
        }catch (IllegalArgumentException ae){
            log.error("解析jwt令牌失败,非法参数异常", ae);
            jsonWebToken.setError(true);
            jsonWebToken.setErrorMessage(ae.getMessage());
        }catch (TokenExpiredException ee){
            log.warn("jwt令牌已过期: {}",ee.getMessage());
            jsonWebToken.setExpired(true);
            jsonWebToken.setExpireTime(ee.getExpiredOn().atZone(ZoneId.systemDefault()).toLocalDateTime());
            jsonWebToken.setErrorMessage("jwt令牌过期,请求重新登录");
        }catch (JWTVerificationException ve){
            log.error("解析jwt令牌出现错误", ve);
            jsonWebToken.setError(true);
            jsonWebToken.setErrorMessage(ve.getMessage());
        }
        return jsonWebToken;
    }
}
